2004年05月05日
川俣晶の縁側ソフトウェア技術雑記 total 10586 count

「あ」がアルファベット扱い? .NET FrameworkのChar.IsDigitとChar.IsLetterの正しい挙動

Written By: 川俣 晶連絡先

 .NET FrameworkのChar.IsLetterは、クラスライブラリリファレンスで、「指定した Unicode 文字がアルファベット文字かどうかを示します」と説明されています。

 では、A~Zとa~zならtrueになる機能かというと、そうではありません。

 同様に、Char.IsDigitは「Unicode 文字が 10 進数の数字かどうかを示します」と説明されていますが、0~9ならtrueになるという訳でもありません。

 少し実際に確認したいと思い、以下のようなプログラムを作って走らせてみました。

 U+0000からU+FFFFまでの範囲のコードに上記の2つのメソッドを適用して、trueを返したコードを出力します。

Char.IsDigitは198個、Char.IsLetterはなんと45443個にヒット §

 結果を見ると分かりますが、Char.IsDigitは198個、Char.IsLetterはなんと45443個にヒットしています。たとえば、半角アルファベットかどうか判定するためにChar.IsLetterを、半角数字かどうか判定するためにChar.IsDigitを使うと、えらい目に遭う、と言うことがこの結果から分かると思います。Cのislphaやisdigitと同じ目的で使うことはできません。

 あまりに長いので、リストは以下のリンク先に置いています。内容は、UTF-8によるプレーンテキストです。

生成結果の全ログ

なぜこんなにも多いのか §

 このような結果になるのは、.NET FrameworkのクラスライブラリがUnicodeの持つ国際性を真面目に実装しようとしたためでしょう。つまり、digitやletterは、0~9やA~Zに限定されるものではなく、世界にはそれに該当する様々な文字があると言うことです。そして、ここでは、ひらがなや漢字ですら、A~Zと同じカテゴリのletterに含まれる、ということでしょう。

 とはいえ、Unicodeでは、このカテゴリをアルファベットではなく、Letterと呼んでいて、実際にメソッド名もIsLetterです。そこから考えると、説明文にアルファベット(英文ではalphabetic letter)と書いてしまったことは、やや混乱を招くような気がしないでもありません。

リストを作成するソース §

 Visual Studio.NET 2003。C#のコンソールアプリケーション。

using System;

using System.IO;

namespace ConsoleApplication23

{

    class Class1

    {

        [STAThread]

        static void Main(string[] args)

        {

            using( StreamWriter writer = new StreamWriter(@"s:\log.txt",false,System.Text.Encoding.UTF8) )

            {

                writer.WriteLine("Char.IsDigitでtrueを返すコード");

                int count = 0;

                for( int i=0; i<=0xffff; i++ )

                {

                    if( Char.IsDigit((char)i) )

                    {

                        writer.WriteLine("U+{0:X} '{1}'",i,(char)i);

                        count ++;

                    }

                }

                writer.WriteLine("****** {0} found",count);

                writer.WriteLine();

                writer.WriteLine("Char.IsLetterでtrueを返すコード");

                int count2 = 0;

                for( int i=0; i<=0xffff; i++ )

                {

                    if( Char.IsLetter((char)i) )

                    {

                        writer.WriteLine("U+{0:X} '{1}'",i,(char)i);

                        count2 ++;

                    }

                }

                writer.WriteLine("****** {0} found",count2);

            }

        }

    }

}